package com.apestech.framework.xml.table.modul;

import com.apestech.api.tables.modul.SeqField;
import com.apestech.framework.db.SqlSession;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.json.JSONOrder;
import com.apestech.framework.sql.SqlUtil;
import com.apestech.framework.sql.callback.MasterSaveInter;
import com.apestech.framework.util.MathUtil;
import com.apestech.framework.util.callback.SpiltStrCallback;

import java.util.ArrayList;

/**
 * @author hup
 * @create 2018-12-28-10:29
 */
public class Tables extends SqlUtil {

    public class Field {
        private String id;
        private String type;
        private boolean notNull;
        private String defaultValue;
        private boolean isKey;
        private String comment;
        private String foreignTab;
        private String foreignKey;

        public Field(Field f){
            this.id=f.getId();
            this.type=f.getType();
            this.notNull=f.isNotNull();
            this.defaultValue=f.getDefaultValue();
            this.isKey=f.isKey();
            this.comment=f.getComment();
            this.foreignTab=f.getForeignTab();
            this.foreignKey=f.getForeignKey();
        }

        public Field(JSONObject map) throws Exception {
            if(map==null) return;
            this.id=map.getString("id");
            this.type=map.getString("type");
            this.notNull=false;
            if("true".equals(map.getString("notNull")))
                this.notNull=true;
            this.defaultValue=map.getString("defaultValue");
            this.isKey=false;
            if("true".equals(map.getString("isKey")))
                this.isKey=true;
            this.comment=map.getString("comment");
            this.foreignTab=map.getString("foreignTab");
            this.foreignKey=map.getString("foreignKey");
        }

        public Field(String fieldId, boolean isKey, com.apestech.framework.dictionary.modul.Table.Field field) throws Exception {
            this.id=fieldId;
            this.type=field.getDbType();
            if(this.type==null && ("VARCHAR2".equals(field.getTypeName()))){
                this.type="VARCHAR2("+field.getLength()+" CHAR)";
            }

            this.notNull=false;
            if("true".equals(field.getNotNull()))
                this.notNull=true;
            this.defaultValue=field.getColumnDef();
            this.isKey=false;
            this.isKey=isKey;
            this.comment=field.getRemarks();
            this.foreignTab=field.getfTab();
            this.foreignKey=field.getfId();
        }

        public void setForeignTab(String foreignTab) {
            this.foreignTab = foreignTab;
        }

        public void setForeignKey(String foreignKey) {
            this.foreignKey = foreignKey;
        }

        public String getId() {
            return id;
        }

        public String getType() {
            return type;
        }

        public boolean isNotNull() {
            return notNull;
        }

        public String getDefaultValue() {
            return defaultValue;
        }

        public boolean isKey() {
            return isKey;
        }

        public String getComment() {
            return comment;
        }

        public String getForeignTab() {
            return foreignTab;
        }

        public String getForeignKey() {
            return foreignKey;
        }
    }

    public class Table{
        private String tabName;
        private String tabComment;
        private String id;
        private ArrayList<Field> fields=new ArrayList<Field>();
        private String primaryField="";
        private JSONObject uniqueIndex=new JSONObject();
        private JSONObject index=new JSONObject();

        public void setTabInfo(JSONObject map){
            tabName=map.getString("tabName");
            tabComment=map.getString("tabComment");
        }

        public void setId(String id) {
            this.id = id;
        }

        public void addField(String fieldId, boolean isKey, com.apestech.framework.dictionary.modul.Table.Field field) throws Exception {
            Field f=new Field(fieldId, isKey, field);
            if(f.isKey){
                if((","+primaryField+",").indexOf(","+f.getId()+",")<0){
                    if(!"".equals(primaryField)) primaryField=primaryField+",";
                    primaryField=primaryField+f.getId();
                }
            }
            fields.add(f);
            index.put(f.getId(), fields.size()-1);
        }

        public void addField(JSONObject map) throws Exception {
            Field f=new Field(map);
            if(f.isKey){
                if((","+primaryField+",").indexOf(","+f.getId()+",")<0){
                    if(!"".equals(primaryField)) primaryField=primaryField+",";
                    primaryField=primaryField+f.getId();
                }
            }
            fields.add(f);
            index.put(f.getId(), fields.size()-1);
        }

        public void setField(Field field) throws Exception {
            int inx=index.getInteger(field.getId());
            fields.set(inx, field);
            if(field.isKey){
                if((","+primaryField+",").indexOf(","+field.id+",")<0){
                    if(!"".equals(primaryField)) primaryField=primaryField+",";
                    primaryField=primaryField+field.getId();
                }
            }
        }

        public void addField(Field field) throws Exception {
            addField(field, false);
        }

        private void addField(Field field, boolean first) throws Exception {
            if(!first) fields.add(field);
            else fields.add(0, field);
            if(field.isKey){
                if((","+primaryField+",").indexOf(","+field.getId()+",")<0){
                    if(!"".equals(primaryField)) primaryField=primaryField+",";
                    primaryField=primaryField+field.getId();
                }
            }
            if(!first) index.put(field.getId(), fields.size()-1);
            else {
                int len=index.size();
                ArrayList<String> list=index.getKeylist();
                for(int i=0; i<len; i++){
                    String k=list.get(i);
                    index.put(k, index.getIntValue(k)+1);
                }
                index.put(field.getId(), 0);
            }
        }

        public void addUniqueIndex(JSONObject map){
            String sufname=map.getString("sufname");
            String fields=map.getString("fields");
            uniqueIndex.put(sufname, fields);
        }

        public JSONObject getUniqueIndex() {
            return uniqueIndex;
        }

        public Field getFieldById(String id){
            if(index.get(id)==null) return null;
            else return fields.get((Integer) index.get(id));
        }

        public int getFieldIndex(String id){
            if(index.get(id)==null) return -1;
            else return (Integer) index.get(id);
        }

        public String getTabName() {
            if(tabName==null) tabName=id;
            return tabName;
        }

        public String getTabComment() {
            return tabComment;
        }

        public String getId() {
            return id;
        }

        public ArrayList<Field> getFields() {
            return fields;
        }

        public String getPrimaryField() {
            return primaryField;
        }

        public void setPrimaryField(String primaryField) {
            this.primaryField = primaryField;
        }

        public ArrayList<String> getColumn(){
            if(index==null) return null;
            return index.getKeylist();
        }
    }

    public class TableRelat{
        private String name;
        private String masterTab;
        private String masterTabName;
        public TableRelat(String name, String masterTab, String masterTabName){
            this.name=name;
            this.masterTab=masterTab;
            this.masterTabName=masterTabName;
        }

        public String getName() {
            return name;
        }

        public String getMasterTab() {
            return masterTab;
        }

        public String getMasterTabName() {
            return masterTabName;
        }
    }

    private String id;
    private JSONObject tables=new JSONObject();
    private JSONOrder relat=new JSONOrder();
    private String firstTable=null;
    private String singletable=null;
    private String masterTable=null;
    private String masterTableId=null;
    private String masterKey=null;
    private String execTable=null;
    private MasterTable master=new MasterTable();
    private String timeField="LAST_UPDATED_STAMP";
    private MasterSaveInter saveInter;
    private int index;
    private SeqField seq;

    public void addTabInfo(String id, JSONObject map){
        Table table=getTable(id);
        table.setTabInfo(map);
        tables.put(id, table);
        if(firstTable==null) firstTable=id;
    }

    public void setSeq(JSONObject map) throws Exception {
        if(seq==null){
            seq=new SeqField();
        }
        seq.setField(map);
    }

    public void setSeq(SeqField seq) throws Exception {
        this.seq=seq;
    }

    public SeqField getSeq() {
        return seq;
    }

    public void setId(String id){
        this.id=id;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void setMaster(String id) {
        Table table=getTable(id);
        table.setId(id);
        tables.put(id, table);
    }

    public void addField(String id, String fieldId, boolean iskey, com.apestech.framework.dictionary.modul.Table.Field field) throws Exception {
        Table table=getTable(id);
        table.addField(fieldId, iskey, field);
        tables.put(id, table);
    }

    public void addField(String id, JSONObject map) throws Exception {
        Table table=getTable(id);
        table.addField(map);
        tables.put(id, table);
    }

    public void addUniqueIndex(String id, JSONObject map){
        Table table=getTable(id);
        table.addUniqueIndex(map);
        tables.put(id, table);
    }

    public String getTabName(String id) {
        Table table=getTable(id);
        if(table!=null){
            if(table.id==null){
                setMaster(id);
                table=getTable(id);
            }
            return table.getTabName();
        }
        return id;
    }

    public String getTabComment(String id) {
        Table table=getTable(id);
        if(table!=null) return table.getTabComment();
        return null;
    }

    public String getId(String id) {
        Table table=getTable(id);
        if(table!=null) return table.getId();
        return null;
    }

    public ArrayList<Field> getFields(String id) {
        Table table=getTable(id);
        if(table!=null) return table.getFields();
        return null;
    }

    public Table getTable(String id){
        if(tables.get(id)==null) return new Table();
        else return (Table) tables.get(id);
    }

    private void setTablePk(String id, String pk){
        Table table=getTable(id);
        if(table!=null) table.setPrimaryField(pk);
        tables.put(id, table);
    }

    public void addMasterTab(String id, String name, String masterTab, String masterTabName){
        if(name==null) name=id;
        if(masterTabName==null) masterTabName=masterTab;
        relat.put(id, new TableRelat(name, masterTab, masterTabName));
        if(masterTab==null) masterTable=id;
        if("2".equals(masterTab)) execTable=name;
    }

    public int size(){
        return tables.size();
    }

    public ArrayList<String> getKeylist(){
        return tables.getKeylist();
    }

    public void expain(SqlSession session) throws Exception {
        if(masterTable==null && size()==1){
            Table tab=(Table)tables.get(tables.getFirstKey());
            addMasterTab(tables.getFirstKey(), tab.getTabName(), "1", "1");
            if(seq!=null){
                seq.setTableId(tables.getFirstKey());
                seq.setTableName(tab.getTabName());
            }
        }
        int len=relat.size();
        if(len==0) return;
        ArrayList<String> klist=relat.getOrderKeylist();
        for(int i=0; i<len; i++){
            String key=klist.get(i);
            TableRelat tableRelat= (TableRelat) relat.get(key);
            if(!"2".equals(tableRelat.getMasterTab())){
                String pkey=getTable(key).getPrimaryField();
                if(session!=null && (pkey==null || pkey.equals(""))){
                    com.apestech.framework.dictionary.modul.Table tab=session.getDictTable(getTabName(key));
                    if(tab!=null){
                        setTablePk(key, tab.getPkKey().getPkColumn());
                    }
                }
                if("1".equals(tableRelat.getMasterTab())){
                    masterTable=key;
                    masterKey=pkey;
                    master.addMaster(key, tableRelat.getName());
                } else{
                    master.addDetail(key, tableRelat.getName(), tableRelat.getMasterTab());
                }

                if(!"1".equals(tableRelat.getMasterTab())){
                    Table master= (Table) tables.get(tableRelat.getMasterTab());
                    if(master!=null) {
                        Table detail= (Table) tables.get(key);
                        String masterField=master.getPrimaryField();
                        if(!"".equals(masterField)){
                            if(masterField.indexOf(",")>=0){
                                String[] flist=masterField.split(",");
                                int flen=flist.length;
                                for(int j=0; j<flen; j++){
                                    String mfield=flist[j];
                                    if(!"".equals(mfield)){
                                        setPrimaryField(master, detail, mfield, j==0);
                                    }
                                }
                            } else {
                                setPrimaryField(master, detail, masterField, true);
                            }
                        }
                    }
                }
            }
        }
    }

    private void setPrimaryField(Table master, Table detail, String field, boolean fkey) throws Exception {
        if(master.getFields().size()==0) return;
        Field f=new Field(master.getFieldById(field));
        if(fkey){
            f.setForeignKey(master.getPrimaryField());
            f.setForeignTab(master.getTabName());
        } else {
            f.setForeignKey(null);
            f.setForeignTab(null);
        }
        if(detail.getFieldById(field)==null) detail.addField(f, true);
        else detail.setField(f);
    }

    public JSONOrder getRelat() {
        return relat;
    }

    public String getFirstTable() {
        return firstTable;
    }

    public boolean isSingleTable(){
        if(singletable==null){
            boolean flag=false;
            int count=relat.size();
            if(count==0) flag=true;
            else if(count==1 && size()==1) flag=true;
            if(flag) singletable="1";
        }
        return "1".equals(singletable);
    }

    public MasterTable getMaster() {
        return master;
    }

    public String getMasterTable() {
        return masterTable;
    }

    public String getExecTable() {
        return execTable;
    }

    public String getMasterKey() {
        return masterKey;
    }

    public JSONObject getSelectMap(){
        JSONObject map=new JSONObject();
        map.put("autoCompletion","0");
        map.put("table_key_where","1");
        return map;
    }

    public JSONObject getTimeMap(){
        JSONObject map=new JSONObject();
        map.put("autoCompletion","0");
        map.put("table_time_field",timeField);
        map.put("table_key_where","1");
        map.put("table_time_control","1");
        return map;
    }

    public String getTimeField() {
        return timeField;
    }

    public void setTimeField(String timeField) {
        if(timeField==null) return;
        this.timeField = timeField;
    }

    public JSONObject getIdMap(JSONObject data) throws Exception {
        JSONObject m=new JSONObject();
        if(data!=null){
            Table table=getTable(masterTable);
            String field=table.getPrimaryField();
            execSplitStr(field, ";", new SpiltStrCallback(){
                @Override
                public void exec(String fid) throws Exception {
                    m.put(fid, data.get(fid));
                }
            });
        }
        return m;
    }

    public JSONObject getIdMap(Object id) throws Exception {
        JSONObject m=new JSONObject();
        if(id!=null){
            Table table=getTable(masterTable);
            String field=table.getPrimaryField();
            execSplitStr(field, ";", new SpiltStrCallback(){
                @Override
                public void exec(String fid) throws Exception {
                    m.put(fid, id);
                }
            });
        }
        return m;
    }

    public JSONObject getMasterIdDataMap(JSONObject data) throws Exception {
        return getIdDataMap(masterTable, data);
    }

    public JSONObject getIdDataMap(String tableName, JSONObject data) throws Exception {
        JSONObject m=new JSONObject();
        if(data!=null){
            Table table=getTable(tableName);
            String pk=table.getPrimaryField();
            execSplitStr(pk, ";", new SpiltStrCallback(){
                @Override
                public void exec(String fid) throws Exception {
                    if(data.get(fid)==null) throw new Exception("主键数据不允许为空！");
                    m.put(fid, data.get(fid));
                }
            });
        }
        return m;
    }

    public JSONObject getParentIdDataMap(String tableName, JSONObject data) throws Exception {
        JSONObject m=new JSONObject();
        if(data!=null){
            String pk=getParentTablePrimary(tableName);
            execSplitStr(pk, ";", new SpiltStrCallback(){
                @Override
                public void exec(String fid) throws Exception {
                    if(data.get(fid)==null) throw new Exception("主键数据不允许为空！");
                    m.put(fid, data.get(fid));
                }
            });
        }
        return m;
    }

    public String getParentTablePrimary(String id){
        String mid=((TableRelat)relat.get(id)).getMasterTab();
        return getTable(mid).getPrimaryField();
    }

    public String getMasterTableId(){
        if(masterTableId==null){
            masterTableId=getTabName(masterTable);
            if(masterTableId==null) masterTableId=((TableRelat)relat.get(relat.getFirstKey())).getName();
        }
        return masterTableId;
    }

    public MasterSaveInter getSaveInter() {
        return saveInter;
    }

    public void setSaveInter(MasterSaveInter saveInter) {
        this.saveInter = saveInter;
    }
}
